package com.tbit.uqbike.util;

/**
 * Created by MyWin on 2017/5/22.
 */
public class TbitTEAUtil {
    private static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(TbitTEAUtil.class);

    private static final int DELTA = 0x9e3779b9;

    private static final int LOOP_TIME = 1;

    private static long MX(long sum, long y, long z, int p, long e, int[] k) {
        return (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[(int) (p & 3 ^ e)] ^ z);
    }

    public static final byte[] KEY = "FB0E689B7A91AF66".getBytes();

    /**
     * Encrypt data with key.
     *
     * @param data
     * @param key
     * @return
     */
    public static byte[] encrypt(byte[] data, byte[] key) {
        if (data.length == 0) {
            return data;
        }
        byte[] dataLenBytes = intToBytes2(data.length);
        byte[] enBytesTmp = new byte[2 + data.length];
        enBytesTmp[0] = dataLenBytes[2];
        enBytesTmp[1] = dataLenBytes[3];
        for (int i = 0; i < data.length; i++) {
            enBytesTmp[i + 2] = data[i];
        }
        byte[] enBytes = toByteArray(encrypt(toIntArray(enBytesTmp), toIntArray(key)));
//		LOGGER.info("Trace encrypt:\t" + bytesToHexString2(enBytes));
        return enBytes;
    }

    /**
     * Decrypt data with key.
     *
     * @param data
     * @param key
     * @return
     */
    public static byte[] decrypt(byte[] data, byte[] key) {
        if (data.length == 0) {
            return data;
        }
        byte[] deBytesTmp = toByteArray(decrypt(toIntArray(data), toIntArray(key)));
        int deLen = (((deBytesTmp[0] & 0xff) << 8) | (deBytesTmp[1] & 0xff));
        //logger.info(String.format("data:%d,body:%d", data.length, deLen));
        if (data.length < deLen) {
            return null;
        }
        byte[] deBytes = new byte[deLen];
        for (int i = 0; i < deLen; i++) {
            deBytes[i] = deBytesTmp[i + 2];
        }
//		LOGGER.info("Trace decrypt:\t" + bytesToHexString2(deBytes));
        return deBytes;
    }

    private static int[] encrypt(int[] v, int[] k) {
        int n = v.length;
        if (n < 1) {
            return v;
        }
        long z = v[n - 1], y = v[0], sum = 0, e;
        int p, q;
        q = LOOP_TIME + 52 / n;

        while (q-- > 0) {
            sum += DELTA;
            e = sum >> 2 & 3;
            for (p = 0; p < n - 1; p++) {
                y = v[p + 1];
                z = v[p] += MX(sum, y, z, p, e, k);
            }
            y = v[0];
            z = v[n - 1] += MX(sum, y, z, p, e, k);
        }
        return v;
    }

    private static int[] decrypt(int[] v, int[] k) {
        int n = v.length;
        if (n < 1) {
            return v;
        }
        int p, q = LOOP_TIME + 52 / n;
        int z = v[n - 1], y = v[0], sum = q * DELTA, e;

        while (sum != 0) {
            e = sum >> 2 & 3;
            for (p = n - 1; p > 0; p--) {
                z = v[p - 1];
                y = v[p] -= MX(sum, y, z, p, e, k);
            }
            z = v[n - 1];
            y = v[0] -= MX(sum, y, z, p, e, k);
            sum = sum - DELTA;
        }
        return v;
    }

    /**
     * Convert byte array to int array.
     *
     * @param data
     * @return
     */
    private static int[] toIntArray(byte[] data) {
        int n = (((data.length & 3) == 0) ? (data.length >>> 2) : ((data.length >>> 2) + 1));

        int[] result = new int[n];
        n = data.length;
        for (int i = 0; i < n; i++) {
            result[i >>> 2] |= (0x000000ff & data[i]) << ((i & 3) << 3);
        }
        return result;
    }

    /**
     * Convert int array to byte array.
     *
     * @param data
     * @return
     */
    private static byte[] toByteArray(int[] data) {
        int n = data.length << 2;
        byte[] result = new byte[n];

        for (int i = 0; i < n; i++) {
            result[i] = (byte) ((data[i >>> 2] >>> ((i & 3) << 3)) & 0xff);
        }
        return result;
    }

    public static final String bytesToHexString(byte[] bArray) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sb.append("0X");
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp.toUpperCase());
            sb.append(" ");
        }
        return sb.toString();
    }

    /**
     * 将int数值转换为占四个字节的byte数组，本方法适用于(低位在前，高位在后)的顺序。 和bytesToInt（）配套使用
     *
     * @param value 要转换的int值
     * @return byte数组
     */
    public static byte[] intToBytes(int value) {
        byte[] src = new byte[4];
        src[3] = (byte) ((value >> 24) & 0xFF);
        src[2] = (byte) ((value >> 16) & 0xFF);
        src[1] = (byte) ((value >> 8) & 0xFF);
        src[0] = (byte) (value & 0xFF);
        return src;
    }

    /**
     * 将int数值转换为占四个字节的byte数组，本方法适用于(高位在前，低位在后)的顺序。 和bytesToInt2（）配套使用
     */
    public static byte[] intToBytes2(int value) {
        byte[] src = new byte[4];
        src[0] = (byte) ((value >> 24) & 0xFF);
        src[1] = (byte) ((value >> 16) & 0xFF);
        src[2] = (byte) ((value >> 8) & 0xFF);
        src[3] = (byte) (value & 0xFF);
        return src;
    }

    /**
     * byte数组转换成16进制字符串
     *
     * @param src
     * @return
     */
    public static String bytesToHexString2(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder();
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    /**
     * byte数组转换成16进制字符数组
     *
     * @param src
     * @return
     */
    public static String[] bytesToHexStrings(byte[] src) {
        if (src == null || src.length <= 0) {
            return null;
        }
        String[] str = new String[src.length];

        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                str[i] = "0";
            }
            str[i] = hv;
        }
        return str;
    }

    public static byte[] hexStringToByte(String hex) {
        int len = (hex.length() / 2);
        byte[] result = new byte[len];
        char[] achar = hex.toCharArray();
        for (int i = 0; i < len; i++) {
            int pos = i * 2;
            result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
        }
        return result;
    }

    private static byte toByte(char c) {
        byte b = (byte) "0123456789ABCDEF".indexOf(c);
        return b;
    }

    public static void main(String[] args) {
        String str = "abcde";
        byte[] data = new byte[]{(byte) 0xEC, (byte) 0xA0, 0x11, 0x71, 0x22, 0x60, 0x00, 0x25};
        // str = "0123456789ABCDEF";
        byte[] keys = "<..!>QINGQI$#@*&".getBytes();
        byte[] bytes = toByteArray(encrypt(toIntArray(data), toIntArray(keys)));
        System.out.println(bytesToHexString2(bytes));
        byte[] data2 = toByteArray(decrypt(toIntArray(bytes), toIntArray(keys)));
        System.out.println(bytesToHexString2(data2));
        String hexStr = "4e6dc2098774b8db60ee19870925073a9dfa030aad67644c3e519b865b348c9797a5eaf7f86bb440d5182c74fb4708d69640485ceea987558524434c8c37fa80221c14ec97a81a9b4307c2133fd0a8b8286fe6c746c1a4e6321abb4a1e66038853548efd3bc30831dbf66d60";
        byte[] hexBytes = hexStringToByte(hexStr);
        byte[] deBytesTmp = toByteArray(decrypt(toIntArray(hexBytes), toIntArray(keys)));
        System.out.println(bytesToHexString2(deBytesTmp));
        byte[] tBytes = {(byte) 0xaa, (byte) 0xaa, (byte) 0x00, (byte) 0x34, (byte) 0x01, (byte) 0x86, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xd2, (byte) 0x19, (byte) 0xa9, (byte) 0xc5, (byte) 0xdc, (byte) 0x87, (byte) 0xea, (byte) 0x7a, (byte) 0xe2, (byte) 0x97, (byte) 0x2a, (byte) 0x0d, (byte) 0xa0, (byte) 0xb8, (byte) 0xab, (byte) 0x40, (byte) 0xea, (byte) 0xc8, (byte) 0x10, (byte) 0xb2, (byte) 0x29, (byte) 0xe8, (byte) 0x17, (byte) 0xba, (byte) 0xea, (byte) 0x8d, (byte) 0x36, (byte) 0xde, (byte) 0xbf, (byte) 0x46, (byte) 0x2d, (byte) 0xa4, (byte) 0xfa, (byte) 0x15, (byte) 0x74, (byte) 0x78, (byte) 0x31, (byte) 0x03, (byte) 0x3b, (byte) 0x4e, (byte) 0x18, (byte) 0xfaaa, (byte) 0xaa, (byte) 0x00, (byte) 0x34, (byte) 0x01, (byte) 0x86, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xd2, (byte) 0x19, (byte) 0xa9, (byte) 0xc5, (byte) 0xdc, (byte) 0x87, (byte) 0xea, (byte) 0x7a, (byte) 0xe2, (byte) 0x97, (byte) 0x2a, (byte) 0x0d, (byte) 0xa0, (byte) 0xb8, (byte) 0xab, (byte) 0x40, (byte) 0xea, (byte) 0xc8, (byte) 0x10, (byte) 0xb2, (byte) 0x29, (byte) 0xe8, (byte) 0x17, (byte) 0xba, (byte) 0xea, (byte) 0x8d, (byte) 0x36, (byte) 0xde, (byte) 0xbf, (byte) 0x46, (byte) 0x2d, (byte) 0xa4, (byte) 0xfa, (byte) 0x15, (byte) 0x74, (byte) 0x78, (byte) 0x31, (byte) 0x03, (byte) 0x3b, (byte) 0x4e, (byte) 0x18, (byte) 0xfa};
        byte[] enTbytes = encrypt(tBytes, keys);
        System.out.println(bytesToHexString2(enTbytes));
        byte[] deTbytes = decrypt(enTbytes, keys);
        System.out.println(bytesToHexString2(deTbytes));
    }
}
